#include "SkpReader.hpp"
#include <SketchUp_i.h>
#include <assert.h>
#include <comdef.h>
#include <iostream>

std::string BSTR_to_string(const BSTR bstr)
{
  _bstr_t temp(bstr);
  std::string result = temp;
  return result;
}

BSTR string_to_BSTR(const std::string& str)
{
  BSTR result;
  _bstr_t temp(str.c_str());
  result = temp.copy();
  return result;
}

SkpEntityProviderStatistics::SkpEntityProviderStatistics(ISkpEntityProviderStatistics* impl)
  : SkpBase<ISkpEntityProviderStatistics>(impl)
{}

long SkpEntityProviderStatistics::numTypes() const
{
  long numTypes;
  HRESULT hr = getImpl()->get_NumberOfTypes(&numTypes);
  assert(hr == S_OK);
  return numTypes;
}

std::string SkpEntityProviderStatistics::getTypeName( StatisticsType typeIndex) const
{
  BSTR name;
  HRESULT hr = getImpl()->GetTypeName((::StatisticsType)typeIndex, &name);
  assert(hr == S_OK);
  return BSTR_to_string(name);
}

long SkpEntityProviderStatistics::getCount(StatisticsType typeIndex, bool includeInstances) const
{
  long count;
  HRESULT hr = getImpl()->GetCount((::StatisticsType)typeIndex, includeInstances, &count);
  assert(hr == S_OK);
  return count;
}


SkpFace::SkpFace(ISkpFace* impl)
  : SkpBase<ISkpFace>(impl)
{}


SkpFaces::SkpFaces(ISkpFaces* impl)
  : SkpBase<ISkpFaces>(impl)
{}

SkpFace SkpFaces::item(long index) 
{
	ISkpFace * pFace;
  HRESULT hr = getImpl()->get_Item(index, &pFace);
	assert(hr == S_OK);
	return SkpFace(pFace);
}

SkpEntityProvider::SkpEntityProvider(ISkpEntityProvider* impl)
  : SkpBase<ISkpEntityProvider>(impl)
{}

SkpFaces SkpEntityProvider::faces() const
{
  ISkpFaces * pFaces = NULL;
  HRESULT hr = getImpl()->get_Faces(&pFaces);
  assert(hr == S_OK);
  return SkpFaces(pFaces);
}

SkpEntityProviderStatistics SkpEntityProvider::statistics() const
{
  ISkpEntityProviderStatistics* stats;
  HRESULT hr = getImpl()->GetStatistics(&stats);
  assert(hr == S_OK);
  return SkpEntityProviderStatistics(stats);
}


SkpDocument::SkpDocument(ISkpDocument* impl)
  : SkpBase<ISkpDocument>(impl)
{}


SkpEntityProvider SkpDocument::entityProvider() const
{
  ISkpEntityProvider* entityProvider = NULL;
  HRESULT hr = getImpl()->QueryInterface(IID_ISkpEntityProvider, (void**)&entityProvider);
  assert(hr == S_OK);
  return SkpEntityProvider(entityProvider);
}

void SkpDocument::print() const
{
  ISkpEntityProvider* pEntityProvider = NULL;
  HRESULT hr = getImpl()->QueryInterface(IID_ISkpEntityProvider, (void**)&pEntityProvider);
  if (pEntityProvider != NULL)
  {
    long nFaces = 0, nEdges = 0;
    ISkpFaces * pFaces = NULL;
    hr = pEntityProvider->get_Faces(&pFaces);
    hr = pFaces->get_Count(&nFaces);
    for (long i = 0; i < nFaces;i++){
      ISkpFace * pFace = NULL;
      pFaces->get_Item(i,&pFace);
      ISkpLoop * pOuterLoop = NULL;
      pFace->get_OuterLoop(&pOuterLoop);
      ISkpEdges * pEdges = NULL;
      pOuterLoop->get_Edges(&pEdges);
      long count;
      pEdges->get_Count(&count);
      for (long i = 0; i < count;i++)
      {
        ISkpEdge * pEdge;
        pEdges->get_Item(i,&pEdge);
        ISkpVertex * pStart,*pEnd;
        pEdge->get_StartVertex(&pStart);
        pEdge->get_EndVertex(&pEnd);
        ISkpPoint3d * pPoint;
        pStart->get_Position(&pPoint);
        double sx,sy,sz,ex,ey,ez;
        pPoint->get_X(&sx);
        pPoint->get_Y(&sy);
        pPoint->get_Z(&sz);
        pEnd->get_Position(&pPoint);
        pPoint->get_X(&ex);
        pPoint->get_Y(&ey);
        pPoint->get_Z(&ez);
        std::cout << "[" << sx << ", " 
          << sy << ", " 
          << sz << "] to [" 
          << ex << ", " 
          << ey << ", " 
          << ez << "]" << std::endl;
      }
    }
  }
}


SkpFileReader::SkpFileReader(ISkpFileReader* impl)
  : SkpBase<ISkpFileReader>(impl)
{}

SkpDocument SkpFileReader::openFile(const std::string& filePath)
{
  ISkpDocument* doc = NULL;
  HRESULT hr = getImpl()->OpenFile(string_to_BSTR(filePath), &doc);
  assert(hr == S_OK);
  return SkpDocument(doc);
}

SkpTextureWriter::SkpTextureWriter(ISkpTextureWriter* impl)
  : SkpBase<ISkpTextureWriter>(impl)
{}

long SkpTextureWriter::count() const
{
  long textureCount;
  HRESULT hr = getImpl()->get_Count(&textureCount);
  assert(hr == S_OK);
  return textureCount;
}

long SkpTextureWriter::loadFace(SkpFace face, bool front) const
{
  long handle = 0;
  HRESULT hr = getImpl()->LoadFace(face.getImpl(), front, &handle); 
  assert(hr == S_OK);
  return handle;
}

std::string SkpTextureWriter::getTextureFile(long textureHandle) const
{
  BSTR fileName;
  HRESULT hr = getImpl()->GetTextureFile(textureHandle, &fileName);
  assert(hr == S_OK);
  return BSTR_to_string(fileName);
}

int SkpTextureWriter::writeAllTextures(std::string directoryName, bool use83) const
{
  HRESULT hr = getImpl()->WriteAllTextures(string_to_BSTR(directoryName),use83);
  assert(hr == S_OK);
  return hr;
}

SkpTextureWriter SkpApplication::createTextureWriter()
{
	ISkpTextureWriter* tw = NULL;
	HRESULT hr = getImpl()->CreateTextureWriter(&tw);
	assert(hr == S_OK);
	return SkpTextureWriter(tw);
} 

SkpApplication SkpApplication::getApplication()
{
  typedef HRESULT (*GetApplication)(ISkpApplication** ppApplication) ;
  
  ISkpApplication* pSkpApp = NULL;
  ISkpFileReader* pFileReader = NULL;

  HMODULE hModule = LoadLibraryEx("SketchUpReader.dll", NULL,LOAD_WITH_ALTERED_SEARCH_PATH);

  GetApplication getApplication = (GetApplication)GetProcAddress(hModule, "GetSketchUpSkpApplication");
  assert(getApplication != NULL);
     
  HRESULT hr = (*getApplication)(&pSkpApp);
  assert(hr == S_OK);

  return SkpApplication(pSkpApp);
}

SkpApplication::SkpApplication(ISkpApplication* impl)
  : SkpBase<ISkpApplication>(impl)
{}

std::string SkpApplication::name() const
{
  BSTR bstr = NULL;
  HRESULT hr = getImpl()->get_Name(&bstr);
  assert(hr == S_OK);
  return BSTR_to_string(bstr);
}

SkpDocument SkpApplication::activeDocument() const
{
  ISkpDocument* doc = NULL;
  HRESULT hr = getImpl()->get_ActiveDocument(&doc);
  assert(hr == S_OK);
  return SkpDocument(doc);
}

SkpFileReader SkpApplication::fileReader() const
{
  ISkpFileReader* fileReader = NULL;
  HRESULT hr = getImpl()->QueryInterface(&fileReader);
  assert(hr == S_OK);
  return SkpFileReader(fileReader);
}



